{
 "cells": [
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "403c07ba",
   "metadata": {},
   "source": [
    "# Callbacks"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "4e5bfd9f",
   "metadata": {},
   "source": [
    "<div class=\"alert alert-block alert-info\">\n",
    "<b>WARNING:</b> This is a new, experimental feature, and the API may change in future.\n",
    "</div>"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "cb2ae3b6",
   "metadata": {},
   "source": [
    "Callbacks provide hooks for users to interact with the different parts of the PyBaMM pipeline, for example to log, save, or visualize outputs of intermediate functions. \n",
    "\n",
    "A list of available callbacks can be found in the [API docs](https://docs.pybamm.org/en/latest/source/api/callbacks.html). Any number of callbacks can be provided as a list, and they will each be called in turn in the order provided.\n",
    "\n",
    "The base class [`pybamm.callbacks.Callback`](https://docs.pybamm.org/en/latest/source/api/callbacks.html#pybamm.callbacks.Callback) documents the available callback methods, at which point in the pipeline they are called, and what arguments are passed to them."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "id": "c25b83cb",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Note: you may need to restart the kernel to use updated packages.\n"
     ]
    }
   ],
   "source": [
    "%pip install \"pybamm[plot,cite]\" -q\n",
    "import pybamm\n",
    "\n",
    "model = pybamm.lithium_ion.DFN()\n",
    "experiment = pybamm.Experiment(\n",
    "    [\n",
    "        (\n",
    "            \"Discharge at C/5 for 10 hours or until 3.3 V\",\n",
    "            \"Charge at 1 A until 4.1 V\",\n",
    "            \"Hold at 4.1 V until 10 mA\",\n",
    "        ),\n",
    "    ]\n",
    "    * 3\n",
    ")\n",
    "sim = pybamm.Simulation(model, experiment=experiment)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "6c38b44e",
   "metadata": {},
   "source": [
    "## Logging callback"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "538f91d4",
   "metadata": {},
   "source": [
    "The `LoggingCallback` can be used to log the progress of the simulation. The default is to print to stdout (this callback is automatically created if no other `LoggingCallback` exists)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "id": "dc4121dd",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-03-10 10:17:57.074 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (18.917 ms elapsed) --------------------\n",
      "2022-03-10 10:17:57.074 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:17:58.139 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:17:58.505 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:17:58.952 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.898 s elapsed) --------------------\n",
      "2022-03-10 10:17:58.953 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:00.203 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:00.464 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:00.675 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.620 s elapsed) --------------------\n",
      "2022-03-10 10:18:00.675 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:01.963 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:02.232 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:02.452 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.620 s\n"
     ]
    }
   ],
   "source": [
    "pybamm.set_logging_level(\"NOTICE\")\n",
    "sim.solve();"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "67608fe3",
   "metadata": {},
   "source": [
    "or to a separate file"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "id": "5e1ecca8",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "2022-03-10 10:18:02.483 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (24.357 ms elapsed) --------------------\n",
      "2022-03-10 10:18:02.483 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:03.799 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:04.065 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:04.394 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.935 s elapsed) --------------------\n",
      "2022-03-10 10:18:04.394 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:05.607 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:05.858 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:06.059 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.600 s elapsed) --------------------\n",
      "2022-03-10 10:18:06.059 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:07.280 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:07.539 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:07.743 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.600 s\n",
      "\n"
     ]
    }
   ],
   "source": [
    "callback = pybamm.callbacks.LoggingCallback(\"output.log\")\n",
    "sim.solve(callbacks=callback)\n",
    "\n",
    "# Read the file that has been written, which was saved to callback.logfile\n",
    "with open(callback.logfile) as f:\n",
    "    print(f.read())\n",
    "\n",
    "# Remove the log file\n",
    "import os\n",
    "\n",
    "os.remove(callback.logfile)"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "aa7b0634",
   "metadata": {},
   "source": [
    "## Custom callbacks"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "070fee9e",
   "metadata": {},
   "source": [
    "Custom callbacks should subclass the class `pybamm.callbacks.Callback` class, and implement a subset of the callback methods `on_<event>`, which all take as input the dictionary `logs`."
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "id": "a47ce3ff",
   "metadata": {},
   "outputs": [],
   "source": [
    "class CustomCallback(pybamm.callbacks.Callback):\n",
    "    def on_experiment_end(self, logs):\n",
    "        print(f\"We are at the end of the simulation. Logs are {logs}\")"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "id": "7f44371e",
   "metadata": {},
   "outputs": [
    {
     "name": "stderr",
     "output_type": "stream",
     "text": [
      "2022-03-10 10:18:07.776 - [NOTICE] callbacks.on_cycle_start(174): Cycle 1/3 (24.415 ms elapsed) --------------------\n",
      "2022-03-10 10:18:07.776 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:09.125 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:09.390 - [NOTICE] callbacks.on_step_start(182): Cycle 1/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:09.714 - [NOTICE] callbacks.on_cycle_start(174): Cycle 2/3 (1.963 s elapsed) --------------------\n",
      "2022-03-10 10:18:09.714 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:11.025 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:11.288 - [NOTICE] callbacks.on_step_start(182): Cycle 2/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:11.498 - [NOTICE] callbacks.on_cycle_start(174): Cycle 3/3 (3.747 s elapsed) --------------------\n",
      "2022-03-10 10:18:11.499 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 1/3: Discharge at C/5 for 10 hours or until 3.3 V\n",
      "2022-03-10 10:18:12.748 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 2/3: Charge at 1 A until 4.1 V\n",
      "2022-03-10 10:18:13.007 - [NOTICE] callbacks.on_step_start(182): Cycle 3/3, step 3/3: Hold at 4.1 V until 10 mA\n",
      "2022-03-10 10:18:13.230 - [NOTICE] callbacks.on_experiment_end(222): Finish experiment simulation, took 3.747 s\n"
     ]
    },
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "We are at the end of the simulation of the simulation. Logs are {'stopping conditions': {'voltage': None, 'capacity': None}, 'cycle number': (3, 3), 'elapsed time': pybamm.TimerTime(3.7469801669999967), 'step number': (3, 3), 'step operating conditions': 'Hold at 4.1 V until 10 mA', 'termination': 'event: Current cut-off (negative) [A] [experiment]', 'summary variables': {'Minimum measured discharge capacity [A.h]': -0.16806782223941116, 'Maximum measured discharge capacity [A.h]': 0.6889979156579616, 'Measured capacity [A.h]': 0.8570657378973728, 'Minimum voltage [V]': 3.300010000000005, 'Maximum voltage [V]': 4.100000000000016, 'Positive electrode capacity [A.h]': 1.9464430360887066, 'Change in positive electrode capacity [A.h]': 0.0, 'Loss of active material in positive electrode [%]': 1.1102230246251565e-14, 'Change in loss of active material in positive electrode [%]': 0.0, 'Loss of lithium inventory [%]': -2.220446049250313e-14, 'Change in loss of lithium inventory [%]': 0.0, 'Loss of lithium inventory, including electrolyte [%]': 0.0, 'Change in loss of lithium inventory, including electrolyte [%]': 0.0, 'Total lithium [mol]': 0.0799932053645051, 'Change in total lithium [mol]': 0.0, 'Total lithium in electrolyte [mol]': 0.002410514999999987, 'Change in total lithium in electrolyte [mol]': -2.168404344971009e-18, 'Total lithium in positive electrode [mol]': 0.037303833837759315, 'Change in total lithium in positive electrode [mol]': 6.938893903907228e-18, 'Total lithium in particles [mol]': 0.07758269036450512, 'Change in total lithium in particles [mol]': 0.0, 'Total lithium lost [mol]': 0.0, 'Change in total lithium lost [mol]': 0.0, 'Total lithium lost from particles [mol]': -1.3877787807814457e-17, 'Change in total lithium lost from particles [mol]': 0.0, 'Total lithium lost from electrolyte [mol]': 1.2576745200831851e-17, 'Change in total lithium lost from electrolyte [mol]': 2.168404344971009e-18, 'Loss of lithium to SEI [mol]': 0.0, 'Change in loss of lithium to SEI [mol]': 0.0, 'Loss of capacity to SEI [A.h]': 0.0, 'Change in loss of capacity to SEI [A.h]': 0.0, 'Total lithium lost to side reactions [mol]': 0.0, 'Change in total lithium lost to side reactions [mol]': 0.0, 'Total capacity lost to side reactions [A.h]': 0.0, 'Change in total capacity lost to side reactions [A.h]': 0.0, 'Local ECM resistance [Ohm]': -0.1921801399643974, 'Change in local ECM resistance [Ohm]': -0.3385111526792064, 'Negative electrode capacity [A.h]': 1.139331489107912, 'Change in negative electrode capacity [A.h]': 0.0, 'Loss of active material in negative electrode [%]': -2.220446049250313e-14, 'Change in loss of active material in negative electrode [%]': 0.0, 'Total lithium in negative electrode [mol]': 0.0402788565267458, 'Change in total lithium in negative electrode [mol]': -6.938893903907228e-18, 'Loss of lithium to lithium plating [mol]': 0.0, 'Change in loss of lithium to lithium plating [mol]': 0.0, 'Loss of capacity to lithium plating [A.h]': 0.0, 'Change in loss of capacity to lithium plating [A.h]': 0.0, 'x_100': 0.9493038520218161, 'y_100': 0.5126064431891194, 'C': 0.8728195070455337, 'x_0': 0.18322346594476246, 'y_0': 0.9610241419672079, 'Un(x_100)': 0.07517904666112207, 'Un(x_0)': 0.2527477655253842, 'Up(y_100)': 4.175179046661121, 'Up(y_0)': 3.357747765525383, 'Up(y_100) - Un(x_100)': 4.099999999999999, 'Up(y_0) - Un(x_0)': 3.1049999999999986, 'n_Li_100': 0.07758269036450512, 'n_Li_0': 0.07758269036450512, 'n_Li': 0.07758269036450512, 'C_n': 1.139331489107912, 'C_p': 1.9464430360887066, 'C_n * (x_100 - x_0)': 0.8728195070455337, 'C_p * (y_100 - y_0)': 0.8728195070455337, 'Capacity [A.h]': 0.8728195070455337}}\n"
     ]
    }
   ],
   "source": [
    "# Note the default `LoggingCallback` is also called\n",
    "sim.solve(callbacks=CustomCallback());"
   ]
  },
  {
   "attachments": {},
   "cell_type": "markdown",
   "id": "f83dccc3",
   "metadata": {},
   "source": [
    "## References\n",
    "\n",
    "The relevant papers for this notebook are:"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "id": "beb72404",
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "[1] Joel A. E. Andersson, Joris Gillis, Greg Horn, James B. Rawlings, and Moritz Diehl. CasADi – A software framework for nonlinear optimization and optimal control. Mathematical Programming Computation, 11(1):1–36, 2019. doi:10.1007/s12532-018-0139-4.\n",
      "[2] Marc Doyle, Thomas F. Fuller, and John Newman. Modeling of galvanostatic charge and discharge of the lithium/polymer/insertion cell. Journal of the Electrochemical society, 140(6):1526–1533, 1993. doi:10.1149/1.2221597.\n",
      "[3] Charles R. Harris, K. Jarrod Millman, Stéfan J. van der Walt, Ralf Gommers, Pauli Virtanen, David Cournapeau, Eric Wieser, Julian Taylor, Sebastian Berg, Nathaniel J. Smith, and others. Array programming with NumPy. Nature, 585(7825):357–362, 2020. doi:10.1038/s41586-020-2649-2.\n",
      "[4] Scott G. Marquis, Valentin Sulzer, Robert Timms, Colin P. Please, and S. Jon Chapman. An asymptotic derivation of a single particle model with electrolyte. Journal of The Electrochemical Society, 166(15):A3693–A3706, 2019. doi:10.1149/2.0341915jes.\n",
      "[5] Peyman Mohtat, Suhak Lee, Jason B Siegel, and Anna G Stefanopoulou. Towards better estimability of electrode-specific state of health: decoding the cell expansion. Journal of Power Sources, 427:101–111, 2019.\n",
      "[6] Valentin Sulzer, Scott G. Marquis, Robert Timms, Martin Robinson, and S. Jon Chapman. Python Battery Mathematical Modelling (PyBaMM). Journal of Open Research Software, 9(1):14, 2021. doi:10.5334/jors.309.\n",
      "[7] Pauli Virtanen, Ralf Gommers, Travis E. Oliphant, Matt Haberland, Tyler Reddy, David Cournapeau, Evgeni Burovski, Pearu Peterson, Warren Weckesser, Jonathan Bright, and others. SciPy 1.0: fundamental algorithms for scientific computing in Python. Nature Methods, 17(3):261–272, 2020. doi:10.1038/s41592-019-0686-2.\n",
      "\n"
     ]
    }
   ],
   "source": [
    "pybamm.print_citations()"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3 (ipykernel)",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.8.12"
  },
  "toc": {
   "base_numbering": 1,
   "nav_menu": {},
   "number_sections": true,
   "sideBar": true,
   "skip_h1_title": false,
   "title_cell": "Table of Contents",
   "title_sidebar": "Contents",
   "toc_cell": false,
   "toc_position": {},
   "toc_section_display": true,
   "toc_window_display": true
  }
 },
 "nbformat": 4,
 "nbformat_minor": 5
}
